// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_render_step_refraction.h"
#include "mk_engine.h"
#include "mk_resource_manager.h"
#include "mk_error.h"
#include "mk_render_device.h"
#include "mk_render_frame.h"
#include "mk_render_target.h"
#include "mk_entity.h"
#include "mk_scene_node.h"
#include "mk_camera.h"
#include "mk_render_step_view_culling.h"
#include "mk_texture.h"

namespace MK3DE
{
	const char* CRenderStepRefraction::RenderStepName = "__RenderStepRefraction";

	CRenderStepRefraction::CRenderStepRefraction(CRenderFrame* renderFrame)
		: CRenderStep(renderFrame, CRenderStepRefraction::RenderStepName)
	{
		_rtRefraction = NULL;
		_refractionTextureSize = 512;
	}

	CRenderStepRefraction::~CRenderStepRefraction()
	{
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		rd->DestroyRenderTarget(_rtRefraction);
	}

	BOOL CRenderStepRefraction::__CheckRefractionRenderTarget()
	{
		if (_rtRefraction)
			return TRUE;

		CEngine* engine = GetEngine();
		CRenderDevice* renderDevice = engine->GetRenderDevice();
		return renderDevice->CreateRenderTarget(_refractionTextureSize, _refractionTextureSize, PF_A8R8G8B8, FALSE, _rtRefraction);
	}

	void CRenderStepRefraction::__MakeRefractionPlane(float height, D3DXPLANE& planeRefraction)
	{
		CCamera* pCamera = GetEngine()->GetCurrentCamera();

		D3DXPlaneFromPointNormal(&planeRefraction, &D3DXVECTOR3(0, height, 0), &D3DXVECTOR3(0, -1, 0));

		D3DXMATRIX matViewIT;
		D3DXMatrixInverse(&matViewIT, NULL, &pCamera->GetViewMatrix());
		D3DXMatrixTranspose(&matViewIT, &matViewIT);

		D3DXMATRIX matProjIT;
		D3DXMatrixInverse(&matProjIT, NULL, &pCamera->GetProjMatrix());
		D3DXMatrixTranspose(&matProjIT, &matProjIT);

		D3DXPlaneTransform(&planeRefraction, &planeRefraction, &(matViewIT * matProjIT));
	}

	void CRenderStepRefraction::Render()
	{
		CRenderStepViewCulling* rsCulling = (CRenderStepViewCulling*)_renderFrame->GetRenderStepByName(CRenderStepViewCulling::RenderStepName);
		CCullingProcessor::CullingResult cullingResult = rsCulling->GetCullingResult();
		std::vector<CEntity*>& vecEntityWater = cullingResult.vecEntity[ERT_WATER];
		if (vecEntityWater.size() == 0)
			return;

		float height = rsCulling->GetVisibleWaterHeight();

		D3DXPLANE planeRefraction;
		__MakeRefractionPlane(height, planeRefraction);

		CEngine* engine = GetEngine();
		CRenderDevice* renderDevice = engine->GetRenderDevice();

		if (!__CheckRefractionRenderTarget())
			return;

		renderDevice->SetRenderTarget(_rtRefraction);
		renderDevice->Clear(TRUE, TRUE, TRUE, MK_COLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
		renderDevice->SetClipPlane(0, planeRefraction, TRUE);

		for (int i = 0; i < (int)ERT_MAX; ++i)
		{
			if (ERT_WATER == (ENTITY_RENDER_TYPE)i)
				continue;

			std::vector<CEntity*>& vecEntity = cullingResult.vecEntity[i];
			std::vector<CEntity*>::iterator it = vecEntity.begin();
			while (it != vecEntity.end())
			{
				CEntity* entity = *it;
				entity->Render();

				++it;
			}
		}		

		renderDevice->SetClipPlane(0, D3DXPLANE(0, 0, 0, 0), FALSE);
	}

	void CRenderStepRefraction::SetRefractionTextureSize(DWORD size)
	{
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		if (_rtRefraction && _refractionTextureSize != size)
			rd->DestroyRenderTarget(_rtRefraction);

		_refractionTextureSize = size;
	}
}